namespace EyeSoft.Domain.Transactional
{
	using System;
	using System.Collections;
	using System.Collections.Generic;
	using System.Linq;
	using System.Linq.Expressions;

	using EyeSoft.Domain.Transactional.Implementations;

	public abstract class UnitOfWork
		: ReadOnlyUnitOfWork
	{
		private readonly ITransactionalCollection transactionalCollection;

		protected UnitOfWork(ITransactionalCollection transactionalCollection)
			: base(transactionalCollection)
		{
			this.transactionalCollection = transactionalCollection;

			AutoInitializeRepository();
		}

		public virtual void Commit()
		{
			transactionalCollection.Commit();
		}

		protected override void AutoInitializeRepository()
		{
			base.AutoInitializeRepository();
			AutoInitializeRepository(typeof(IRepository<>), "Repository");
		}

		protected IRepository<T> Repository<T>()
			where T : class, IAggregate
		{
			return new Repository<T>(new InternalRepository<T>(ReadRepository<T>(), transactionalCollection));
		}

		private class InternalRepository<T> : IRepository<T>
			where T : class, IAggregate
		{
			private readonly IReadOnlyRepository<T> readRepository;

			private readonly ITransactionalCollection transactionalCollection;

			public InternalRepository(
				IReadOnlyRepository<T> readRepository,
				ITransactionalCollection transactionalCollection)
			{
				this.readRepository = readRepository;
				this.transactionalCollection = transactionalCollection;
			}

			public Expression Expression
			{
				get { return readRepository.Expression; }
			}

			public Type ElementType
			{
				get { return readRepository.ElementType; }
			}

			public IQueryProvider Provider
			{
				get { return readRepository.Provider; }
			}

			public T GetByKey(IComparable key)
			{
				return readRepository.GetByKey(key);
			}

			public IEnumerator<T> GetEnumerator()
			{
				return readRepository.GetEnumerator();
			}

			IEnumerator IEnumerable.GetEnumerator()
			{
				return GetEnumerator();
			}

			public void Add(T entity)
			{
				transactionalCollection.Add(entity);
			}

			public bool Remove(T entity)
			{
				transactionalCollection.Delete(entity);
				return true;
			}

			public void Update(T entity)
			{
				transactionalCollection.Update(entity);
			}
		}
	}
}